{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "h2q27gKz1H20"
      },
      "source": [
        "##### Copyright 2024 The AI Edge Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TUfAcER1oUS6"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Gb7qyhNL1yWt"
      },
      "source": [
        "# Text Searcher with TensorFlow Lite Model Maker"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Fw5Y7snSuG51"
      },
      "source": [
        "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://www.tensorflow.org/lite/models/modify/model_maker/text_searcher\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" /\u003eView on TensorFlow.org\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/models/modify/model_maker/text_searcher.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eRun in Google Colab\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/models/modify/model_maker/text_searcher.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eView source on GitHub\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca href=\"https://storage.googleapis.com/tensorflow_docs/tensorflow/tensorflow/lite/g3doc/models/modify/model_maker/text_searcher.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/download_logo_32px.png\" /\u003eDownload notebook\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca href=\"https://tfhub.dev/google/universal-sentence-encoder-lite/2\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/hub_logo_32px.png\" /\u003eSee TF Hub model\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "\u003c/table\u003e"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c2sdIlXEVPZR"
      },
      "source": [
        "In this colab notebook, you can learn how to use the [TensorFlow Lite Model Maker](https://www.tensorflow.org/lite/models/modify/model_maker) library to create a TFLite Searcher model. You can use a text Searcher model to build Semantic Search or Smart Reply for your app. This type of model lets you take a text query and search for the most related entries in a text dataset, such as a database of web pages. The model returns a list of the smallest distance scoring entries in the dataset, including metadata you specify, such as URL, page title, or other text entry identifiers. After building this, you can deploy it onto devices (e.g. Android) using [Task Library Searcher API](https://www.tensorflow.org/lite/inference_with_metadata/task_library/text_searcher) to run inference with just a few lines of code.\n",
        "\n",
        "This tutorial leverages CNN/DailyMail dataset as an instance to create the TFLite Searcher model. You can try with your own dataset with the compatible input comma separated value (CSV) format."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "A_aZYF85VaVK"
      },
      "source": [
        "## Text search using Scalable Nearest Neighbor"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sr3q-gvm3cI8"
      },
      "source": [
        "This tutorial uses the publicly available CNN/DailyMail non-anonymized summarization dataset, which was produced from the [GitHub repo](https://github.com/abisee/cnn-dailymail). This dataset contains over 300k news articles, which makes it a good dataset to build the Searcher model, and return various related news during model inference for a text query.\n",
        "\n",
        "The text Searcher model in this example uses a [ScaNN](https://github.com/google-research/google-research/tree/master/scann) (Scalable Nearest Neighbors) index file that can search for similar items from a predefined database. ScaNN achieves state-of-the-art performance for efficient vector similarity search at scale.\n",
        "\n",
        "Highlights and urls in this dataset are used in this colab to create the model:\n",
        "\n",
        "1. Highlights are the text for generating the embedding feature vectors and then used for search.\n",
        "2. Urls are the returned result shown to users after searching the related highlights.\n",
        "\n",
        "This tutorial saves these data into the CSV file and then uses the CSV file to build the model. Here are several examples from the dataset.\n",
        "\n",
        "\n",
        "|  Highlights  | Urls\n",
        "| ---------- |----------\n",
        "|Hawaiian Airlines again lands at No. 1 in on-time performance. The Airline Quality Rankings Report looks at the 14 largest U.S. airlines. ExpressJet \u003cbr\u003e and American Airlines had the worst on-time performance. Virgin America had the best baggage  handling; Southwest had lowest complaint rate. | http://www.cnn.com/2013/04/08/travel/airline-quality-report\n",
        "| European football's governing body reveals list of countries bidding to host 2020 finals. The 60th anniversary edition of the finals will be hosted by 13 \u003cbr\u003e countries. Thirty-two countries are considering bids to host 2020 matches. UEFA will announce host cities on September 25. | http://edition.cnn.com:80/2013/09/20/sport/football/football-euro-2020-bid-countries/index.html?\n",
        "| Once octopus-hunter Dylan Mayer has now also signed a petition of 5,000 divers banning their hunt at Seacrest Park. Decision by Washington \u003cbr\u003e Department of Fish and Wildlife could take months. | http://www.dailymail.co.uk:80/news/article-2238423/Dylan-Mayer-Washington-considers-ban-Octopus-hunting-diver-caught-ate-Puget-Sound.html?\n",
        "| Galaxy was observed 420 million years after the Big Bang. found by NASA’s Hubble Space Telescope, Spitzer Space Telescope, and one of nature’s \u003cbr\u003e own natural 'zoom lenses' in space. | http://www.dailymail.co.uk/sciencetech/article-2233883/The-furthest-object-seen-Record-breaking-image-shows-galaxy-13-3-BILLION-light-years-Earth.html\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bcLF2PKkSbV3"
      },
      "source": [
        "## Setup\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2vvAObmTqglq"
      },
      "source": [
        "Start by installing the required packages, including the Model Maker package from the [GitHub repo](https://github.com/tensorflow/examples/tree/master/tensorflow_examples/lite/model_maker)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qhl8lqVamEty"
      },
      "outputs": [],
      "source": [
        "!sudo apt -y install libportaudio2\n",
        "!pip install -q tflite-model-maker\n",
        "!pip install gdown"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "l6lRhVK9Q_0U"
      },
      "source": [
        "Import the required packages."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "XtxiUeZEiXpt"
      },
      "outputs": [],
      "source": [
        "from tflite_model_maker import searcher"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "veelLw_VT6uQ"
      },
      "source": [
        "### Prepare the dataset\n",
        "\n",
        "This tutorial uses the dataset CNN / Daily Mail summarization dataset from the [GitHub repo](https://github.com/abisee/cnn-dailymail).\n",
        "\n",
        "First, download the text and urls of cnn and dailymail and unzip them. If it\n",
        "failed to download from google drive, please wait a few minutes to try it again or download it manually and then upload it to the colab."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-P3zxue1T6Iy"
      },
      "outputs": [],
      "source": [
        "!gdown https://drive.google.com/uc?id=0BwmD_VLjROrfTHk4NFg2SndKcjQ\n",
        "!gdown https://drive.google.com/uc?id=0BwmD_VLjROrfM1BxdkxVaTY2bWs\n",
        "\n",
        "!wget -O all_train.txt https://raw.githubusercontent.com/abisee/cnn-dailymail/master/url_lists/all_train.txt\n",
        "!tar xzf cnn_stories.tgz\n",
        "!tar xzf dailymail_stories.tgz"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aoOWzTU7ViPM"
      },
      "source": [
        "Then, save the data into the CSV file that can be loaded into `tflite_model_maker` library. The code is based on the logic used to load this data in [`tensorflow_datasets`](https://github.com/tensorflow/datasets/blob/master/tensorflow_datasets/summarization/cnn_dailymail.py). We can't use `tensorflow_dataset` directly since it doesn't contain urls which are used in this colab.\n",
        "\n",
        "Since it takes a long time to process the data into embedding feature vectors\n",
        "for the whole dataset. Only first 5% stories of CNN and Daily Mail dataset are\n",
        "selected by default for demo purpose. You can adjust the\n",
        "fraction or try with the pre-built TFLite [model](https://storage.googleapis.com/download.tensorflow.org/models/tflite_support/searcher/text_to_image_blogpost/cnn_daily_text_searcher.tflite) with 50% stories of CNN and Daily Mail dataset to search as well."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "bA4PsR6NVU69"
      },
      "outputs": [],
      "source": [
        "#@title Save the highlights and urls to the CSV file\n",
        "#@markdown Load the highlights from the stories of CNN / Daily Mail, map urls with highlights, and save them to the CSV file.\n",
        "\n",
        "CNN_FRACTION = 0.05 #@param {type:\"number\"}\n",
        "DAILYMAIL_FRACTION = 0.05 #@param {type:\"number\"}\n",
        "\n",
        "import csv\n",
        "import hashlib\n",
        "import os\n",
        "import tensorflow as tf\n",
        "\n",
        "dm_single_close_quote = u\"\\u2019\"  # unicode\n",
        "dm_double_close_quote = u\"\\u201d\"\n",
        "END_TOKENS = [\n",
        "    \".\", \"!\", \"?\", \"...\", \"'\", \"`\", '\"', dm_single_close_quote,\n",
        "    dm_double_close_quote, \")\"\n",
        "]  # acceptable ways to end a sentence\n",
        "\n",
        "\n",
        "def read_file(file_path):\n",
        "  \"\"\"Reads lines in the file.\"\"\"\n",
        "  lines = []\n",
        "  with tf.io.gfile.GFile(file_path, \"r\") as f:\n",
        "    for line in f:\n",
        "      lines.append(line.strip())\n",
        "  return lines\n",
        "\n",
        "\n",
        "def url_hash(url):\n",
        "  \"\"\"Gets the hash value of the url.\"\"\"\n",
        "  h = hashlib.sha1()\n",
        "  url = url.encode(\"utf-8\")\n",
        "  h.update(url)\n",
        "  return h.hexdigest()\n",
        "\n",
        "\n",
        "def get_url_hashes_dict(urls_path):\n",
        "  \"\"\"Gets hashes dict that maps the hash value to the original url in file.\"\"\"\n",
        "  urls = read_file(urls_path)\n",
        "  return {url_hash(url): url[url.find(\"id_/\") + 4:] for url in urls}\n",
        "\n",
        "\n",
        "def find_files(folder, url_dict):\n",
        "  \"\"\"Finds files corresponding to the urls in the folder.\"\"\"\n",
        "  all_files = tf.io.gfile.listdir(folder)\n",
        "  ret_files = []\n",
        "  for file in all_files:\n",
        "    # Gets the file name without extension.\n",
        "    filename = os.path.splitext(os.path.basename(file))[0]\n",
        "    if filename in url_dict:\n",
        "      ret_files.append(os.path.join(folder, file))\n",
        "  return ret_files\n",
        "\n",
        "\n",
        "def fix_missing_period(line):\n",
        "  \"\"\"Adds a period to a line that is missing a period.\"\"\"\n",
        "  if \"@highlight\" in line:\n",
        "    return line\n",
        "  if not line:\n",
        "    return line\n",
        "  if line[-1] in END_TOKENS:\n",
        "    return line\n",
        "  return line + \".\"\n",
        "\n",
        "\n",
        "def get_highlights(story_file):\n",
        "  \"\"\"Gets highlights from a story file path.\"\"\"\n",
        "  lines = read_file(story_file)\n",
        "\n",
        "  # Put periods on the ends of lines that are missing them\n",
        "  # (this is a problem in the dataset because many image captions don't end in\n",
        "  # periods; consequently they end up in the body of the article as run-on\n",
        "  # sentences)\n",
        "  lines = [fix_missing_period(line) for line in lines]\n",
        "\n",
        "  # Separate out article and abstract sentences\n",
        "  highlight_list = []\n",
        "  next_is_highlight = False\n",
        "  for line in lines:\n",
        "    if not line:\n",
        "      continue  # empty line\n",
        "    elif line.startswith(\"@highlight\"):\n",
        "      next_is_highlight = True\n",
        "    elif next_is_highlight:\n",
        "      highlight_list.append(line)\n",
        "\n",
        "  # Make highlights into a single string.\n",
        "  highlights = \"\\n\".join(highlight_list)\n",
        "\n",
        "  return highlights\n",
        "\n",
        "url_hashes_dict = get_url_hashes_dict(\"all_train.txt\")\n",
        "cnn_files = find_files(\"cnn/stories\", url_hashes_dict)\n",
        "dailymail_files = find_files(\"dailymail/stories\", url_hashes_dict)\n",
        "\n",
        "# The size to be selected.\n",
        "cnn_size = int(CNN_FRACTION * len(cnn_files))\n",
        "dailymail_size = int(DAILYMAIL_FRACTION * len(dailymail_files))\n",
        "print(\"CNN size: %d\"%cnn_size)\n",
        "print(\"Daily Mail size: %d\"%dailymail_size)\n",
        "\n",
        "with open(\"cnn_dailymail.csv\", \"w\") as csvfile:\n",
        "  writer = csv.DictWriter(csvfile, fieldnames=[\"highlights\", \"urls\"])\n",
        "  writer.writeheader()\n",
        "\n",
        "  for file in cnn_files[:cnn_size] + dailymail_files[:dailymail_size]:\n",
        "    highlights = get_highlights(file)\n",
        "    # Gets the filename which is the hash value of the url.\n",
        "    filename = os.path.splitext(os.path.basename(file))[0]\n",
        "    url = url_hashes_dict[filename]\n",
        "    writer.writerow({\"highlights\": highlights, \"urls\": url})\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xushUyZXqP59"
      },
      "source": [
        "## Build the text Searcher model"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vn61LJ9QbOPi"
      },
      "source": [
        "Create a text Searcher model by loading a dataset, creating a model with the data and exporting the TFLite model.\n",
        "\n",
        "### Step 1. Load the dataset\n",
        "\n",
        "Model Maker takes the text dataset and the corresponding metadata of each text string (such as urls in this example) in the CSV format. It embeds the text strings into feature vectors using the user-specified embedder model.\n",
        "\n",
        "In this demo, we build the Searcher model using [Universal Sentence Encoder](https://tfhub.dev/google/universal-sentence-encoder-lite/2), a state-of-the-art sentence embedding model which is already retrained from [colab](https://github.com/tensorflow/tflite-support/blob/master/tensorflow_lite_support/examples/colab/on_device_text_to_image_search_tflite.ipynb). The model is optimized for on-device inference performance, and only takes 6ms to embed a query string (measured on Pixel 6). Alternatively, you can use [this](https://tfhub.dev/google/lite-model/universal-sentence-encoder-qa-ondevice/1?lite-format=tflite) quantized version, which is smaller but takes 38ms for each embedding."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1ymHbk0wjHHZ"
      },
      "outputs": [],
      "source": [
        "!wget -O universal_sentence_encoder.tflite https://storage.googleapis.com/download.tensorflow.org/models/tflite_support/searcher/text_to_image_blogpost/text_embedder.tflite"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "knJwmJHxkFbx"
      },
      "source": [
        "Create a `searcher.TextDataLoader` instance and use `data_loader.load_from_csv` method to load the dataset. It takes ~10 minutes for this\n",
        "step since it generates the embedding feature vector for each text one by one. You can try to upload your own CSV file and load it to build the customized model as well.\n",
        "\n",
        "Specify the name of text column and metadata column in the CSV file.\n",
        "* Text is used to generate the embedding feature vectors.\n",
        "* Metadata is the content to be shown when you search the certain text.\n",
        "\n",
        "Here are the first 4 lines of the CNN-DailyMail CSV file generated above.\n",
        "\n",
        "| highlights| urls\n",
        "| ---------- |----------\n",
        "|Syrian official: Obama climbed to the top of the tree, doesn't know how to get down. Obama sends a letter to the heads of the House and Senate. Obama \u003cbr\u003e to seek congressional approval on military action against Syria. Aim is to determine whether CW were used, not by whom, says U.N. spokesman.|http://www.cnn.com/2013/08/31/world/meast/syria-civil-war/\n",
        "|Usain Bolt wins third gold of world championship. Anchors Jamaica to 4x100m relay victory. Eighth gold at the championships for Bolt. Jamaica double \u003cbr\u003e up in women's 4x100m relay.|http://edition.cnn.com/2013/08/18/sport/athletics-bolt-jamaica-gold\n",
        "|The employee in agency's Kansas City office is among hundreds of \"virtual\" workers. The employee's travel to and from the mainland U.S. last year cost \u003cbr\u003e more than $24,000. The telecommuting program, like all GSA practices, is under review.|http://www.cnn.com:80/2012/08/23/politics/gsa-hawaii-teleworking\n",
        "|NEW: A Canadian doctor says she was part of a team examining Harry Burkhart in 2010. NEW: Diagnosis: \"autism, severe anxiety, post-traumatic stress \u003cbr\u003e disorder and depression\" Burkhart is also suspected in a German arson probe, officials say. Prosecutors believe the German national set a string of fires \u003cbr\u003e in Los Angeles.|http://edition.cnn.com:80/2012/01/05/justice/california-arson/index.html?\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CtdZ-JDwMimd"
      },
      "outputs": [],
      "source": [
        "data_loader = searcher.TextDataLoader.create(\"universal_sentence_encoder.tflite\", l2_normalize=True)\n",
        "data_loader.load_from_csv(\"cnn_dailymail.csv\", text_column=\"highlights\", metadata_column=\"urls\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VVN5bkSFiZdV"
      },
      "source": [
        "For image use cases, you can create a `searcher.ImageDataLoader` instance and then use `data_loader.load_from_folder` to load images from the folder. The `searcher.ImageDataLoader` instance needs to be created by a TFLite embedder model because it will be leveraged to encode queries to feature vectors and be exported with the TFLite Searcher model. For instance:\n",
        "```python\n",
        "data_loader = searcher.ImageDataLoader.create(\"mobilenet_v2_035_96_embedder_with_metadata.tflite\")\n",
        "data_loader.load_from_folder(\"food/\")\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2uZkLR6N6gDR"
      },
      "source": [
        "###Step 2. Create the Searcher model\n",
        "\n",
        "* Configure ScaNN options. See [api doc](https://www.tensorflow.org/lite/api_docs/python/tflite_model_maker/searcher/ScaNNOptions) for more details.\n",
        "* Create the Searcher model from data and ScaNN options. You can see the [in-depth examination](https://ai.googleblog.com/2020/07/announcing-scann-efficient-vector.html) to learn more about the ScaNN algorithm."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kwlYdTcg63xy"
      },
      "outputs": [],
      "source": [
        "scann_options = searcher.ScaNNOptions(\n",
        "      distance_measure=\"dot_product\",\n",
        "      tree=searcher.Tree(num_leaves=140, num_leaves_to_search=4),\n",
        "      score_ah=searcher.ScoreAH(dimensions_per_block=1, anisotropic_quantization_threshold=0.2))\n",
        "model = searcher.Searcher.create_from_data(data_loader, scann_options)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lluAWms3soFm"
      },
      "source": [
        "In the above example, we define the following options:\n",
        "* `distance_measure`: we use \"dot_product\" to measure the distance between two embedding vectors. Note that we actually compute the **negative** dot product value to preserve the notion that \"smaller is closer\".\n",
        "\n",
        "* `tree`: the dataset is divided the dataset into 140 partitions (roughly the square root of the data size), and 4 of them are searched during retrieval, which is roughly 3% of the dataset.\n",
        "\n",
        "* `score_ah`: we quantize the float embeddings to int8 values with the same dimension to save space."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CgCDMe0e6jlT"
      },
      "source": [
        "###Step 3. Export the TFLite model\n",
        "\n",
        "Then you can export the TFLite Searcher model."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Hm_UULdW7A9T"
      },
      "outputs": [],
      "source": [
        "model.export(\n",
        "      export_filename=\"searcher.tflite\",\n",
        "      userinfo=\"\",\n",
        "      export_format=searcher.ExportFormat.TFLITE)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "me6_RwPZqNhX"
      },
      "source": [
        "## Test the TFLite model on your query\n",
        "\n",
        "You can test the exported TFLite model using custom query text. To query text using the Searcher model, initialize the model and run a search with text phrase, as follows:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "GkXtipXKqXp4"
      },
      "outputs": [],
      "source": [
        "from tflite_support.task import text\n",
        "\n",
        "# Initializes a TextSearcher object.\n",
        "searcher = text.TextSearcher.create_from_file(\"searcher.tflite\")\n",
        "\n",
        "# Searches the input query.\n",
        "results = searcher.search(\"The Airline Quality Rankings Report looks at the 14 largest U.S. airlines.\")\n",
        "print(results)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GzI0tPM2rFlc"
      },
      "source": [
        "See the [Task Library documentation](https://www.tensorflow.org/lite/inference_with_metadata/task_library/text_searcher) for more information about how to integrate the model to various platforms."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HS4u77W5gnzQ"
      },
      "source": [
        "# Read more\n",
        "\n",
        " For more information, please refer to:\n",
        "\n",
        "*   TensorFlow Lite Model Maker [guide](https://www.tensorflow.org/lite/models/modify/model_maker) and [API reference](https://www.tensorflow.org/lite/api_docs/python/tflite_model_maker).\n",
        "\n",
        "*   Task Library: [TextSearcher](https://www.tensorflow.org/lite/inference_with_metadata/task_library/text_searcher) for deployment.\n",
        "*   The end-to-end reference apps: [Android](https://github.com/tensorflow/examples/tree/master/lite/examples/text_searcher/android).\n"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "Model Maker Text Searcher Tutorial",
      "provenance": [
        {
          "file_id": "1dbRXQCjtm-jBFC32DJ6YCVXnXBOG3M5t",
          "timestamp": 1613441434239
        },
        {
          "file_id": "https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/models/modify/model_maker/text_classification.ipynb",
          "timestamp": 1612303859066
        }
      ],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
